@file:JvmName("UIContextExtKt")

package com.sea.base.ext.view

import android.app.Activity
import android.content.ComponentName
import android.content.Intent
import android.os.Environment
import android.provider.MediaStore
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResult
import androidx.annotation.IdRes
import androidx.annotation.MainThread
import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.sea.base.activities.BaseKtActivity
import com.sea.base.ext.global.defAuthorFileProviderSt
import com.sea.base.ext.global.getCurrentMillis
import com.sea.base.ext.global.intentNew
import com.sea.base.ext.global.log
import com.sea.base.ext.global.singleApplication
import com.sea.base.ext.global.throwIfDebug
import com.sea.base.ext.obs.addAfterObserver
import com.sea.base.ext.obs.doOnCreated
import com.sea.base.ext.obs.doOnDestroyed
import com.sea.base.ext.obs.doOnNextResumed
import com.sea.base.ext.obs.doOnResumed
import com.sea.base.fragment.FragmentVisibilityChild
import com.sea.base.start.StartActivityForResultHelper
import com.sea.base.ui.IUIContext
import com.sea.base.utils.ActivityStackManager
import com.sea.base.utils.KeyboardUtils
import java.io.File

/**
 * 关掉当前页面（fragment则关掉对应Activity）
 */
@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.finish() {
    this.activity?.finish()
}

@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.hideKeyboard() {
    (rootView ?: ActivityStackManager.getCurrentActivity()?.window?.decorView)?.let { KeyboardUtils.hideKeyboard(it) }
}

/**
 * create之后执行，只执行一次
 */
fun IUIContext.doOnCreated(callback: Runnable) {
    viewLifecycleWithCallback {
        it?.doOnCreated(callback)
    }
}

/**
 * 执行在resume时（如果已经resume过会立即执行）
 * 等于doOnVisible（不可见时不会回调）
 */
fun IUIContext.doOnResumed(callback: Runnable) {
    viewLifecycleWithCallback {
        it?.doOnResumed(callback)
    }
}

/**
 * 执行在下一次resume后（忽略之前的resume）
 */
fun IUIContext.doOnNextResumed(callback: Runnable) {
    viewLifecycleWithCallback {
        it?.doOnNextResumed(callback)
    }
}

fun IUIContext.doOnDestroyed(callback: Runnable) {
    viewLifecycleWithCallback {
        if (it == null) {
            callback.run()
        } else {
            it.doOnDestroyed(callback)
        }
    }
}

/**
 * 键盘显隐监听
 * 目前一个Fragment/Activity/View只能有一个监听，你可以指定多个view进行多监听[setOnKeyboardChangedListener]
 * @param onChangedListener true:键盘弹出，false键盘收起
 */
@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.setOnKeyboardChangedListener(noinline onChangedListener: (Boolean) -> Unit) {
    rootView?.setOnKeyboardChangedListener(onChangedListener)
}

inline val IUIContext.isKeyboardShowing get() = rootView?.isKeyboardShowing ?: false

inline fun IUIContext.doOnKeyboardVisible(crossinline onVisibleListener: () -> Unit) {
    rootView?.doOnKeyboardVisible(onVisibleListener)
}

inline fun IUIContext.doOnKeyboardHide(crossinline onHideListener: () -> Unit) {
    rootView?.doOnKeyboardHide(onHideListener)
}

/**
 * @param intent 你的Intent，不需要传context和Class（如果传了将会被覆盖）
 * @param params 可以继续传参
 */
inline fun <reified T> IUIContext.startActivity(intent: Intent? = null, params: (Intent.() -> Unit) = {}) {
    try {
        val realIntent = intent ?: Intent()
        realIntent.component = ComponentName(requireActivity, T::class.java)
        params(realIntent)
        startActivity(realIntent)
    } catch (e: Throwable) {
        e.printStackTrace()
        "跳转异常".throwIfDebug(e)
    }
}

/**
 * 获取context，如果没有则为application
 */
inline val IUIContext.requireContext
    get() = context ?: singleApplication

/**
 * Activity的非null情况，当然你必须明确知道Activity存在
 */
inline val IUIContext.requireActivity
    get() = activity ?: throw IllegalStateException("this $this not attached to an activity.")

inline val IUIContext.requireRootView
    get() = rootView ?: throw IllegalStateException("this $this not attached to an rootView.")

/**
 * 根据生命周期自动管理初始化
 * 注意：请在super.onCreate后和super.onDestroy前使用
 * 注意：可以在子线程调用，调用期间会强制阻塞子线程
 */
@Suppress("NOTHING_TO_INLINE")
inline fun <T> IUIContext.lazyWithUI(noinline initializer: () -> T) =
    com.sea.base.ui.UILazyDelegate(this, initializer)

/**
 * 加载自身的vm
 */
inline fun <reified VM : ViewModel> IUIContext.lazyVM(): Lazy<VM> {
    return lazyWithUI { getVM() }
}

/**
 * 如果你已经确定在create之后了，可以直接获取
 */
inline fun <reified VM : ViewModel> IUIContext.getVM() = ViewModelProvider(this)[VM::class.java]

/**
 * 加载Activity的vm
 * 当你用Activity的vm时需要考虑如下：
 * 1.它会缓存到Activity上
 * 2.如果是Activity的同一个vm可能会互相影响
 * 3.注意observer的粘性问题
 */
inline fun <reified VM : ViewModel> IUIContext.lazyActivityVM(): Lazy<VM> {
    return lazyWithUI { ViewModelProvider(requireActivity)[VM::class.java] }
}

@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.hideFragment(fragment: Fragment) {
    hideFragment(fragment.javaClass)
}

@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.hideFragment(clazz: Class<out Fragment>) {
    val ft: FragmentTransaction = currentFragmentManager.beginTransaction()
    val currentFragment: Fragment? = currentFragmentManager.findFragmentByTag(clazz.name)
    currentFragment?.let {
        ft.hide(it)
        ft.commitNow()
    }
}

@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.removeFragment(fragment: Fragment) {
    removeFragment(fragment.javaClass)
}

@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.removeFragment(clazz: Class<out Fragment>) {
    val ft: FragmentTransaction = currentFragmentManager.beginTransaction()
    val currentFragment: Fragment? = currentFragmentManager.findFragmentByTag(clazz.name)
    currentFragment?.let {
        ft.remove(it)
        ft.commitNow()
    }
}

inline fun IUIContext.addBackPressedCallback(crossinline callback: () -> Unit) {
    this.requireActivity.onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            callback.invoke()
        }
    })
}


inline fun IUIContext.setResultOk(params: (Intent.() -> Unit) = {}) {
    requireActivity.setResult(Activity.RESULT_OK, Intent().apply(params))
}

@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.setResultCancel() {
    requireActivity.setResult(Activity.RESULT_CANCELED)
}

/**
 * 这只是提供回调的功能，没有转换功能，所以建议在跳转的类里创建公开转换代码
 * 注意：请确定[BaseKtActivity.supportRebuildData]为false，如果支持重建请使用自带的[BaseKtActivity.registerForActivityResult]
 *
 * 示例如下（activity需要id参数并返回字符串）：
 *  companion object {
 *      fun startResult(ui: IUIContext, id: Long, onResultOkCallback: (String) -> Unit) {
 *          ui.startActivityForResultCancelOk<TwoActivity>({
 *              putExtra("id", id)
 *          }) {
 *              onResultOkCallback.invoke(it?.getStringExtra("result") ?: return@startActivityForResultCancelOk)
 *          }
 *      }
 *  }
 */
@MainThread
@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.startActivityForResult(
    intent: Intent,
    noinline resultCallback: (ActivityResult) -> Unit
) {
    StartActivityForResultHelper.startActivityForResult(activity, intent, resultCallback)
}

@MainThread
inline fun <reified T : Activity> IUIContext.startActivityForResult(
    params: (Intent.() -> Unit) = {},
    noinline resultCallback: (ActivityResult) -> Unit
) {
    StartActivityForResultHelper.startActivityForResult(activity, intentNew<T>().apply(params), resultCallback)
}

@MainThread
inline fun <reified T : Activity> IUIContext.startActivityForResultCancelOk(
    params: (Intent.() -> Unit) = {},
    crossinline onCancel: () -> Unit = {},
    crossinline onOk: (Intent?) -> Unit
) {
    StartActivityForResultHelper.startActivityForResult(activity, intentNew<T>().apply(params)) {
        if (it.resultCode == Activity.RESULT_OK) {
            onOk.invoke(it.data)
        } else {
            onCancel.invoke()
        }
    }
}

/**
 * 当前页面对于用户而言是否可见
 */
inline val IUIContext.isVisibleToUser get() = lifecycle.currentState == Lifecycle.State.RESUMED

/**
 * 页面可见变化监听，会立即调用一次
 * 第一次可见可以使用[doOnResumed]
 * @return 基于Lifecycle，你可以根据返回值主动取消监听
 */
@MainThread
inline fun IUIContext.addOnVisibleChangedListener(crossinline onChangedCallback: LifecycleEventObserver.(isVisible: Boolean) -> Unit) =
    addNextOnVisibleChangedListener(onChangedCallback).also { it.onChangedCallback(isVisibleToUser) }

/**
 * 添加后续的页面可见性变化监听
 * 第一次可见可以使用[doOnResumed]
 * @return 基于Lifecycle，你可以根据返回值主动取消监听
 */
@MainThread
inline fun IUIContext.addNextOnVisibleChangedListener(crossinline onChangedCallback: LifecycleEventObserver.(isVisible: Boolean) -> Unit) =
    lifecycle.addAfterObserver { _, event ->
        when (event) {
            Lifecycle.Event.ON_RESUME -> this.onChangedCallback(true)
            Lifecycle.Event.ON_PAUSE -> this.onChangedCallback(false)
            else -> {}
        }
    }

inline fun IUIContext.postMainSmartLifecycle(crossinline call: () -> Unit) {
    com.sea.base.ext.global.postMainSmartLifecycle(this, call)
}

/**
 * 延时后等待可见执行可组合[doOnResumed]实现
 */
@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.postMainDelayedLifecycle(mills: Long, run: Runnable) {
    this.viewLifecycleWithCallback {
        com.sea.base.ext.global.postMainDelayedLifecycle(it ?: return@viewLifecycleWithCallback, mills, run)
    }
}

/**
 * 拍照
 */
inline fun IUIContext.startCameraForResult(crossinline onResult: (file: File) -> Unit) {
    val file = File(singleApplication.getExternalFilesDir(Environment.DIRECTORY_DCIM), "${getCurrentMillis()}.jpg")
    startActivityForResult(Intent().apply {
        action = MediaStore.ACTION_IMAGE_CAPTURE
        addCategory(Intent.CATEGORY_DEFAULT)
        putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(singleApplication, defAuthorFileProviderSt, file))
    }) {
        if (file.exists()) {
            onResult.invoke(file)
        } else {
            "拍照失败".log()
        }
    }
}


inline val IUIContext.currentFragment: Fragment?
    get() {
        currentFragmentManager.fragments.forEach {
            if (it !is FragmentVisibilityChild) {
                throw IllegalStateException("该方法只适合继承FragmentVisibilityChild的Fragment")
            }
            if (it.isVisibleParent) {
                return it
            }
        }
        return null
    }

/**
 * @throws IllegalArgumentException 请确保你的Fragment存在，并且是[FragmentVisibilityChild]的子类
 */
@Suppress("NOTHING_TO_INLINE")
inline fun IUIContext.switchFragment(tag: String, @IdRes frameLayoutId: Int = 0): Fragment {
    val fragment = currentFragmentManager.findFragmentByTag(tag)!!
    fragment as FragmentVisibilityChild
    switchFragment(fragment, frameLayoutId)
    return fragment
}

/**
 * 切换fragment，如果没有缓存则新建，如果未show则show出来，如果show中则什么都不做
 * 默认使用currentFragment.hasCode 作为tag
 */
@Suppress("NOTHING_TO_INLINE")
inline fun <T> IUIContext.switchFragment(
    newFrag: T,
    @IdRes frameLayoutId: Int = 0,
    tag: String? = null
) where T : Fragment, T : FragmentVisibilityChild {
    val safeTag = tag ?: newFrag.hashCode().toString()
    val ft: FragmentTransaction = currentFragmentManager.beginTransaction()
    currentFragment?.let {
        if (it == newFrag) return
        ft.hide(it)
    }
    if (newFrag.isAdded) {
        ft.show(newFrag)
    } else if (frameLayoutId != 0) {
        ft.add(frameLayoutId, newFrag, safeTag)
    }
    ft.commitNow()
}